package io.lvdaxian.mybatis11.builder.xml;

import io.lvdaxian.mybatis11.builder.BaseBuilder;
import io.lvdaxian.mybatis11.datasource.DataSourceFactory;
import io.lvdaxian.mybatis11.io.Resources;
import io.lvdaxian.mybatis11.mapping.Environment;
import io.lvdaxian.mybatis11.session.Configuration;
import io.lvdaxian.mybatis11.transaction.TransactionFactory;
import io.lvdaxian.mybatis11.type.TypeAliasRegistry;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Properties;

/**
 * 此方法是 xml config 构建者
 *
 * @author lihh
 */
public class XMLConfigBuilder extends BaseBuilder {
  
  private final Element root;
  
  public XMLConfigBuilder(Reader reader) {
    // 初期化的时候 实例化 Configuration
    super(new Configuration());
    
    // dom4j 处理 xml
    SAXReader saxReader = new SAXReader();
    try {
      // 解析 入口的reader
      Document document = saxReader.read(new InputSource(reader));
      // 拿到 根标签
      root = document.getRootElement();
    } catch (DocumentException e) {
      throw new RuntimeException(e);
    }
  }
  
  public Configuration parseConfiguration() {
    try {
      // 解析 数据源标签
      environmentsElement(root.element("environments"));
      // 从这里开始 解析别名
      typeAliasesElement(root.element("typeAliases"));
      
      // 这里开始 解析总的标签【mappers】
      mapperElement(root.element("mappers"));
    } catch (IOException | DocumentException | InstantiationException |
             IllegalAccessException e) {
      throw new RuntimeException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
    return configuration;
  }
  
  /**
   * 解析 环境标签的方法
   *
   * @param context 解析environments 根标签
   * @author lihh
   */
  private void environmentsElement(Element context) throws InstantiationException, IllegalAccessException {
    // 从这里 获取到默认值, 这是默认激活的的数据源
    String environment = context.attributeValue("default");
    // 筛选 多数据源 从 xml 配置中解析出 environment
    List<Element> environmentList = context.elements("environment");
    // 从配置中 拿到 类型别名注册器
    TypeAliasRegistry typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    
    // 遍历 多个环境配置
    for (Element e : environmentList) {
      String id = e.attributeValue("id");
      
      // 表示 筛选到了 默认设置的环境
      if (!environment.equals(id))
        continue;
      
      // 拿到事务工厂
      TransactionFactory txFactory = (TransactionFactory) typeAliasRegistry.resolveAlias(e.element("transactionManager").attributeValue("type")).newInstance();
      
      // 从 这里拿到数据源 Element标签
      Element dataSourceElement = e.element("dataSource");
      // 数据源工厂, 这里数据源工厂 已经注册到 typeAliasRegistry 别名集合中了
      DataSourceFactory dataSourceFactory = (DataSourceFactory) typeAliasRegistry.resolveAlias(
              dataSourceElement.attributeValue("type")
          )
          .newInstance();
      // 拿到属性[property] 原则上是可以拿到 多个property
      List<Element> propertyList = dataSourceElement.elements("property");
      
      Properties props = new Properties();
      propertyList.forEach(el -> {
        String name = el.attributeValue("name"),
            value = el.attributeValue("value");
        
        props.setProperty(name, value);
      });
      // 给 数据源工厂 设置属性
      dataSourceFactory.setProperties(props);
      // 拿到数据源
      DataSource dataSource = dataSourceFactory.getDataSource();
      
      // 构建 数据源环境
      Environment.Builder environmentBuilder = new Environment
          .Builder(id)
          .transactionFactory(txFactory)
          .dataSource(dataSource);
      
      // 配置文件 设置环境
      configuration.setEnvironment(environmentBuilder.build());
    }
  }
  
  /**
   * 这是解析别名的位置
   *
   * @param aliasElement 别名的 element
   * @author lihh
   */
  private void typeAliasesElement(Element aliasElement) {
    if (null == aliasElement)
      return;
    
    List<Element> elements = aliasElement.elements("package");
    if (null != elements) {
      for (Element element : elements) {
        String typeAliasPackage = element.attributeValue("name");
        configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
      }
    }
    
    // 这里尝试拿到 typeAliases
    List<Element> typeAlias = aliasElement.elements("typeAlias");
    for (Element element : typeAlias) {
      String alias = element.attributeValue("alias");
      String type = element.attributeValue("type");
      
      try {
        Class<?> clazz = Resources.classForName(type);
        
        // 这是不配置别名的时候
        if (null == alias)
          configuration.getTypeAliasRegistry().registerAlias(clazz);
        else
          configuration.getTypeAliasRegistry().registerAlias(alias, clazz);
      } catch (Exception e) {
        throw new RuntimeException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
      }
    }
  }
  
  /**
   * 从这里开始 处理 mapper element
   *
   * @param mappers element 元素
   * @author lihh
   */
  private void mapperElement(Element mappers) throws IOException, DocumentException {
    // 从这里解析单个 mapper
    List<Element> mapperList = mappers.elements("mapper");
    
    for (Element e : mapperList) {
      // 从 mapper 中拿到 resource
      // resource 这个也是缓存的标识
      String resource = e.attributeValue("resource");
      InputStream inputStream = Resources.getResourceAsStream(resource);
      
      // 在for循环里每个mapper都重新new一个XMLMapperBuilder，来解析
      XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource);
      mapperParser.parse();
    }
  }
}
